#include <iostream>
#include <cstring>
#include <string>
#include <iostream>
#include <iomanip>

// 非对称加密头文件
#include <openssl/rsa.h>
#include <openssl/pem.h>


// 生成RSA密钥对 并 写入文件
void rsa_test()
{
    // 创建RSA对象 包含私钥和公钥
    RSA* rsa = RSA_new();
    // 创建BIGNUM对象，用于存储公钥指数
    BIGNUM* bn = BN_new();
    BN_set_word(bn, RSA_F4); //初始化公钥指数 比如RSA_F4=65537

    //生成RSA密钥对
    //bits:秘钥的长度，单位：bit 一般为 1024 * n(n为整数) 秘钥长度增加一倍，公钥操作一般增加4倍，私钥操作增加8倍
    //e:公钥指数 一般在5位数以内
    RSA_generate_key_ex(rsa, 1024, bn, NULL);
    BN_free(bn);
    
    // 将密钥对的 base64 写入文件
    FILE* fp = fopen("public_key.pem", "w");
    PEM_write_RSAPublicKey(fp, rsa);
    fclose(fp);
    fp = fopen("private_key.pem", "w");
    PEM_write_RSAPrivateKey(fp, rsa, NULL, NULL, 0, NULL, NULL);
    fclose(fp);

    // 使用 bio 对象在文件中写入秘钥
    BIO* bio = BIO_new_file("bio_public_key.pem", "w");
    PEM_write_bio_RSAPublicKey(bio, rsa);
    BIO_free_all(bio);
    bio = BIO_new_file("bio_private_key.pem", "w");
    PEM_write_bio_RSAPrivateKey(bio, rsa, NULL, NULL, 0, NULL, NULL);
    BIO_free_all(bio);

    RSA_free(rsa);
}

std::string to_hex(const unsigned char* data, size_t len) {
    std::ostringstream oss;
    for (size_t i = 0; i < len; ++i)
        oss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(data[i]);
    return oss.str();
}

//公钥加密
std::string rsa_encrypt_pub()
{
    std::string plaintext = "hello world";

    //使用 bio 方式读取公钥
    BIO* bio = BIO_new_file("public_key.pem", "r");
    RSA* pubKey = RSA_new();
    /*
        bio:指向BIO对象的指针
        rsa:指向RSA对象的指针
        cb:回调函数，用于处理错误
        u:回调函数参数
    */
    PEM_read_bio_RSAPublicKey(bio, &pubKey, NULL, NULL);
    BIO_free_all(bio);

    //加密 数据加密之后长度和秘钥长度相同
    uint keyLen = RSA_size(pubKey); //获取公钥长度
    char* to = new char[keyLen];
    //返回值为密文的长度
    int len = RSA_public_encrypt(plaintext.size(), (const unsigned char *)plaintext.data(), 
        (unsigned char *)to, pubKey, RSA_PKCS1_PADDING);

    RSA_free(pubKey);

    std::cout << "密文打印：" << to_hex((const unsigned char*)to, keyLen) << std::endl;

    return std::string(to, len);
}

//私钥解密
std::string rsa_decrypt_pri(std::string ciphertext)
{
    BIO *bio = BIO_new_file("private_key.pem", "r");
    RSA* priKey = RSA_new();
    PEM_read_bio_RSAPrivateKey(bio, &priKey, NULL, NULL);
    BIO_free_all(bio);

    uint keyLen = RSA_size(priKey);
    char* to = new char[keyLen];
    //返回值为原始数据的长度
    int len = RSA_private_decrypt(ciphertext.size(), (const unsigned char *)ciphertext.data(), 
        (unsigned char *)to, priKey, RSA_PKCS1_PADDING);

    std::cout << "原始数据长度：" << len << std::endl;
    return std::string(to, len);
}


int main(void)
{
    rsa_test();
    std::string s1 = rsa_encrypt_pub();
    std::string s2 = rsa_decrypt_pri(s1);
    std::cout << "解密后：" << s2 << std::endl;

    return 0;
}